﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using System.Windows.Forms;
using Microsoft.Win32;
using QTPlugin;
using QTPlugin.Interop;
using System.Windows.Forms.Design;
using Kawanon.Foundation;
using System.Globalization;
using Kawanon.Localization;

namespace Kawanon.QtHoney
{
	public static class Utility
	{
		const uint GW_HWNDFIRST = 0;
		const uint GW_HWNDLAST = 1;
		const uint GW_HWNDNEXT = 2;
		const uint GW_HWNDPREV = 3;
		const uint GW_OWNER = 4;
		const uint GW_CHILD = 5;
		const uint GW_ENABLEDPOPUP = 6;

		private const uint S_OK = 0;

		const string CFSTR_SHELLIDLIST = "Shell IDList Array";

		const uint CF_HDROP = 15;

		[DllImport("user32.dll", SetLastError = true)]
		static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);

		[DllImport("user32.dll", CharSet = CharSet.Auto)]
		static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);

		[DllImport("user32.dll")]
		[return: MarshalAs(UnmanagedType.Bool)]
		static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);

		[DllImport("user32.dll", CharSet = CharSet.Unicode)]
		static extern uint RegisterClipboardFormat(string lpszFormat);

		[DllImport("kernel32.dll")]
		static extern IntPtr GlobalLock(IntPtr hMem);

		[DllImport("kernel32.dll")]
		static extern bool GlobalUnlock(IntPtr hMem);

		[DllImport("ole32.dll")]
		static extern void ReleaseStgMedium(ref STGMEDIUM pmedium);

		[DllImport("shell32.dll")]
		static extern IntPtr ILClone(IntPtr pidl);

		[DllImport("shell32.dll")]
		static extern IntPtr ILCombine(IntPtr pidl1_ABSOLUTE, IntPtr pidl2_RELATIVE);

		[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
		static extern IntPtr ILCreateFromPath(string pszPath);

		[DllImport("shell32.dll")]
		static extern IntPtr ILFindLastID(IntPtr pidl);

		[DllImport("shell32.dll")]
		static extern uint ILGetSize(IntPtr pidl);

		[DllImport("shell32.dll")]
		static extern void ILFree(IntPtr pidl_RELATIVE);

		[DllImport("shell32.dll")]
		static extern uint DragQueryFile(IntPtr hDrop, uint iFile, [Out] StringBuilder lpszFile, uint cch);

		[DllImport("shell32.dll")]
		static extern uint DragQueryFile(IntPtr hDrop, uint iFile, IntPtr zero, uint cch);

		[DllImport("ole32.dll")]
		static extern IntPtr CoTaskMemAlloc(uint cb);

		[DllImport("ole32.dll")]
		static extern void CoTaskMemFree(IntPtr pv);

		[DllImport("shell32.dll")]
		[return: MarshalAs(UnmanagedType.Bool)]
		static extern bool SHGetPathFromIDListW(IntPtr pidl, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder pszPath);

		private struct SHFILEINFO
		{
			public IntPtr hIcon;
			public int iIcon;
			public uint dwAttributes;
			[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
			public string szDisplayName;
			[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
			public string szTypeName;
		}

		private const uint SHGFI_ICON = 0x100; // アイコン・リソースの取得
		private const uint SHGFI_LARGEICON = 0x0; // 大きいアイコン
		private const uint SHGFI_SMALLICON = 0x1; // 小さいアイコン
		private const uint SHGFI_SYSICONINDEX = 0x000004000;
		private const uint SHGFI_DISPLAYNAME = 0x000000200;
		private const uint SHGFI_PIDL = 0x000000008;

		[DllImport("shell32.dll")]
		private static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbSizeFileInfo, uint uFlags);

		[DllImport("shell32.dll")]
		private static extern IntPtr SHGetFileInfo(IntPtr pidl, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbSizeFileInfo, uint uFlags);

		[DllImport("user32.dll", SetLastError = true)]
		[return: MarshalAs(UnmanagedType.Bool)]
		static extern bool DestroyIcon(IntPtr hIcon);

		struct BROWSEINFO
		{
			public IntPtr hwndOwner;
			public IntPtr pidlRoot;
			public string pszDisplayName;
			public string lpszTitle;
			public uint ulFlags;
			public IntPtr lpfn;
			public IntPtr lParam;
			public int iImage;
		}

		private const uint BIF_RETURNONLYFSDIRS = 0x0001;  // For finding a folder to start document searching
		private const uint BIF_DONTGOBELOWDOMAIN = 0x0002;  // For starting the Find Computer
		private const uint BIF_STATUSTEXT = 0x0004;  // Top of the dialog has 2 lines of text for BROWSEINFO.lpszTitle and one line if
		// this flag is set.  Passing the message BFFM_SETSTATUSTEXTA to the hwnd can set the
		// rest of the text.  This is not used with BIF_USENEWUI and BROWSEINFO.lpszTitle gets
		// all three lines of text.
		private const uint BIF_RETURNFSANCESTORS = 0x0008;
		private const uint BIF_EDITBOX = 0x0010;   // Add an editbox to the dialog
		private const uint BIF_VALIDATE = 0x0020;   // insist on valid result (or CANCEL)

		private const uint BIF_NEWDIALOGSTYLE = 0x0040;   // Use the new dialog layout with the ability to resize
		// Caller needs to call OleInitialize() before using this API
		private const uint BIF_USENEWUI = 0x0040 + 0x0010; //(BIF_NEWDIALOGSTYLE | BIF_EDITBOX);

		private const uint BIF_BROWSEINCLUDEURLS = 0x0080;   // Allow URLs to be displayed or entered. (Requires BIF_USENEWUI)
		private const uint BIF_UAHINT = 0x0100;   // Add a UA hint to the dialog, in place of the edit box. May not be combined with BIF_EDITBOX
		private const uint BIF_NONEWFOLDERBUTTON = 0x0200;   // Do not add the "New Folder" button to the dialog.  Only applicable with BIF_NEWDIALOGSTYLE.
		private const uint BIF_NOTRANSLATETARGETS = 0x0400;  // don't traverse target as shortcut

		private const uint BIF_BROWSEFORCOMPUTER = 0x1000;  // Browsing for Computers.
		private const uint BIF_BROWSEFORPRINTER = 0x2000;// Browsing for Printers
		private const uint BIF_BROWSEINCLUDEFILES = 0x4000; // Browsing for Everything
		private const uint BIF_SHAREABLE = 0x8000;  // sharable resources displayed (remote shares, requires BIF_USENEWUI)

		[DllImport("shell32.dll")]
		static extern IntPtr SHBrowseForFolder(ref BROWSEINFO lpbi);

		[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
		static extern uint SHGetPathFromIDList(IntPtr pidl, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszPath);

		[DllImport("shell32.dll")]
		static extern int SHGetDesktopFolder(ref IShellFolder ppshf);

		private const uint SHGDN_NORMAL = 0x0000;
		private const uint SHGDN_INFOLDER       = 0x0001;
		private const uint SHGDN_FOREDITING     = 0x1000;
		private const uint SHGDN_FORADDRESSBAR    = 0x4000;
		private const uint SHGDN_FORPARSING     = 0x8000;

		[DllImport("shlwapi")]
		public static extern Int32 StrRetToBuf(
			ref STRRET pstr,
			IntPtr pidl,
			StringBuilder pszBuf,
			uint cchBuf);

		private const uint WM_SETREDRAW = 0x000B;

		[DllImport("user32")]
		private static extern bool SendMessage(IntPtr hWnd, uint msg, bool wParam, IntPtr lParam);

		[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
		static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);

		[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
		static extern int GetWindowTextLength(IntPtr hWnd);

		//[StructLayout(LayoutKind.Sequential)]
		//struct CIDA
		//{
		//    /// <summary>
		//    ///  Number of PIDLs that are being transferred, not counting the parent folder.
		//    /// </summary>
		//    public uint cidl;

		//    /// <summary>
		//    /// An array of offsets, relative to the beginning of this structure. The array contains
		//    /// cidl+1 elements. The first element of aoffset contains an offset to the fully-qualified
		//    /// PIDL of a parent foloder. If this PIDL is empty, the parent folder is the desktop.
		//    /// Each of the remaining elements of the array contains an offset to one of the PIDLs to be
		//    /// transferred. ALL of these PIDLs are relative to the PIDL of the parent folder.
		//    /// </summary>
		//    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
		//    public uint[] aoffset;
		//}

		public static void ShowForm(IPluginServer server, Form form)	// from GroupEditor
		{
			RECT rc = new RECT();
			if (GetWindowRect(server.ExplorerHandle, out rc))
			{
				form.StartPosition = FormStartPosition.Manual;
				form.Left = rc.left + (rc.Width - form.Width) / 2;
				form.Top = rc.top + (rc.Height - form.Height) / 2;
			}
			else
			{
				form.StartPosition = FormStartPosition.CenterScreen;
			}

			// ツールバーウィンドウを親に（ExplorerHandle は null が返るので）
			Control parent = Control.FromHandle(server.Handle);
			form.Show(parent);

			//MessageBox.Show(string.Format("{0} {1} {2} {3}", rc.right, rc.top, parent.Width, parent.Height ));
		}

		public static Control GetBandObject(IPluginServer server)
		{
			// QTTabBarClass を取得
			return Control.FromHandle(server.Handle);
		}

		public static IShellBrowser GetShellBrowser(IPluginServer server)
		{
			return (IShellBrowser)Reflection.GetField(server, "shellBrowser");
		}

		public static bool IsDebugMode()
		{
			RegistryKey key = Plugins.OpenRegistryKey(false);
			if (key == null)
			{
				return false;
			}
			int val = (int)key.GetValue("Debug", 0);
			key.Close();
			return val != 0;
		}

		public delegate T ModalInvoker<T>();

		public static T DoModal<T>(IPluginServer server, ModalInvoker<T> func)
		{
			if (server != null)
			{
				server.ExecuteCommand(Commands.SetModalState, true);
			}
			try
			{
				return func();
			}
			finally
			{
				if (server != null)
				{
					server.ExecuteCommand(Commands.SetModalState, false);
				}
			}
		}

		public static DialogResult DoModal(IPluginServer server, Form form)
		{
			if (server != null)
			{
				server.ExecuteCommand(Commands.SetModalState, true);
			}
			try
			{
				return form.ShowDialog();
			}
			finally
			{
				if (server != null)
				{
					server.ExecuteCommand(Commands.SetModalState, false);
				}
			}
		}

		public static string GetClassName(IntPtr hwnd)
		{
			StringBuilder buf = new StringBuilder(32);
			if (GetClassName(hwnd, buf, 32) == 0)
			{
				return null;
			}
			return buf.ToString();
		}

		public static Predicate<IntPtr> EqualsClassName(string name)
		{
			return delegate(IntPtr hwnd)
			{
				string className = GetClassName(hwnd);
				if (className == null)
				{
					return false;
				}
				return className == name;
			};
		}

		public static IntPtr FindChildWindow(IntPtr hwnd, Predicate<IntPtr> pred)
		{
			IntPtr child = GetWindow(hwnd, GW_CHILD);
			while (child != IntPtr.Zero)
			{
				if (pred(child))
				{
					return child;
				}
				// 孫以下を検索 (フォルダに共通の作業を表示する の場合など)
				IntPtr ret = FindChildWindow(child, pred);
				if (ret != IntPtr.Zero)
				{
					return ret;
				}

				child = GetWindow(child, GW_HWNDNEXT);
			}
			return IntPtr.Zero;
		}

		private delegate T ExtractHGlobalHandler<T>(IntPtr hGlobal);

		private static T ExtractHGlobal<T>(System.Runtime.InteropServices.ComTypes.IDataObject data,
			uint cfFormat, ExtractHGlobalHandler<T> handler)
			where T : class
		{
			FORMATETC format = new FORMATETC();
			format.cfFormat = (short)cfFormat;
			format.dwAspect = DVASPECT.DVASPECT_CONTENT;
			format.lindex = -1;
			format.tymed = TYMED.TYMED_HGLOBAL;

			if (data.QueryGetData(ref format) != S_OK)
			{
				return null;
			}
			STGMEDIUM medium = new STGMEDIUM();
			data.GetData(ref format, out medium);

			IntPtr hGlobal = GlobalLock(medium.unionmember);
			if (hGlobal == IntPtr.Zero)
			{
				return null;
			}

			T ret;
			try
			{
				ret = handler.Invoke(hGlobal);
			}
			finally
			{
				GlobalUnlock(hGlobal);
				ReleaseStgMedium(ref medium);
			}
			return ret;
		}

		public static string[] GetFileNames(System.Runtime.InteropServices.ComTypes.IDataObject data)
		{
			return ExtractHGlobal(data, CF_HDROP, delegate(IntPtr hDrop)
			{
				List<string> list = new List<string>();
				StringBuilder buf = new StringBuilder(260);
				uint count = DragQueryFile(hDrop, UInt32.MaxValue, IntPtr.Zero, 0);
				for (uint i = 0; i < count; i++)
				{
					uint len = DragQueryFile(hDrop, i, IntPtr.Zero, 0);
					buf.Capacity = Math.Max(buf.Capacity, (int)len);

					if (DragQueryFile(hDrop, i, buf, (uint)buf.Capacity) != len)
					{
						Dbg.Abort("DragQueryFile length error");
					}
					list.Add(buf.ToString());
				}
				return list.ToArray();
			});
		}

		public static Address[] GetAddresses(System.Runtime.InteropServices.ComTypes.IDataObject data)
		{
			return ExtractHGlobal(data, RegisterClipboardFormat(CFSTR_SHELLIDLIST), delegate(IntPtr hGlobal)
			{
				int intSize = Marshal.SizeOf(typeof(int));
				int cidl = Marshal.ReadInt32(hGlobal, 0);

				List<IntPtr> pointers = new List<IntPtr>();
				for (int i = 0; i < cidl + 1; i++)
				{	// 親パスも含める
					int offset = Marshal.ReadInt32(hGlobal, (i + 1) * intSize);	// cidl をスキップ
					//byte[] bytes = new byte[offset - prev];
					//Marshal.Copy(new IntPtr( (int)hGlobal + offset ), bytes, 0, bytes.Length);
					//list.Add(bytes);
					//prev = offset;

					//Address addr = new Address(new IntPtr((int)hGlobal + offset));
					//addr.ToString();
					pointers.Add(new IntPtr((int)hGlobal + offset));
				}
				IntPtr parent = pointers[0];
				pointers.RemoveAt(0);
				pointers = pointers.ConvertAll(ptr => ILCombine(parent, ptr));

				List<Address> addrs = pointers.ConvertAll(ptr => new Address(ptr));
				pointers.ForEach(ptr => ILFree(ptr));	// 解放

				return addrs.ToArray();
			});
		}

		public static string AddressToPath(Address addr)
		{
			if (addr.Path != null)
			{
				//Dbg.Echo(addr.Path);
				return addr.Path;
			}
			if (addr.ITEMIDLIST == null)
			{
				//Dbg.Abort("ITEMIDLIST is null");
				return null;
			}

			return PidlToPath(addr.ITEMIDLIST);
		}

		public static string PidlToPath(byte[] bytes)
		{
			//int len = bytes.Length;
			//IntPtr pidl = CoTaskMemAlloc((uint)len);
			//Marshal.Copy(bytes, 0, pidl, len);
			//try
			//{
			//    string path = PidlToPath(pidl);
			//    return path;
			//}
			//finally
			//{
			//    CoTaskMemFree(pidl);
			//}

			return ForPidl<string>(bytes, PidlToPath);
		}

		public static string PidlToPath(IntPtr pidl)
		{
			IShellFolder desk = null;
			SHGetDesktopFolder(ref desk);
			STRRET ret = new STRRET();
			if (desk.GetDisplayNameOf(pidl, SHGDN_FORPARSING, out ret) != S_OK)
			{
				//Dbg.Abort("GetDisplayNameOf error");
				return null;
			}
			StringBuilder buf = new StringBuilder(260);
			if (StrRetToBuf(ref ret, pidl, buf, (uint)buf.Capacity) != S_OK)
			{
				Dbg.Abort("StrRetToBuf error");
			}
			return buf.ToString();
		}

		public static string Quote(string str)
		{
			return '"' + str + '"';
		}

		public static uint MakeLong(uint a, uint b)
		{
			return ((uint)(((ushort)(a)) | ((uint)((ushort)(b))) << 16));
		}

		public static int MakeLong(int a, int b)
		{
			return (int)MakeLong((uint)a, (uint)b);
		}

		public static bool IsSpecialFolder(string path)
		{
			return path.StartsWith("::{");
		}

		public static string GetDisplayName(string path)
		{
			return GetDisplayName(PathToPidl(path));
		}

		public static void ForPidl(byte[] bytes, Action<IntPtr> func)
		{
			IntPtr pidl = CoTaskMemAlloc((uint)bytes.Length);
			Marshal.Copy(bytes, 0, pidl, bytes.Length);
			try
			{
				func(pidl);
			}
			finally
			{
				ILFree(pidl);
			}
		}

		public static T ForPidl<T>(byte[] bytes, Converter<IntPtr, T> func)
		{
			IntPtr pidl = CoTaskMemAlloc((uint)bytes.Length);
			Marshal.Copy(bytes, 0, pidl, bytes.Length);
			try
			{
				return func(pidl);
			}
			finally
			{
				ILFree(pidl);
			}
		}

		public static string GetDisplayName(byte[] bytes)
		{
			if (bytes == null)	// null を許容
			{
				return null;
			}
			return ForPidl(bytes, delegate(IntPtr pidl)
			{
				SHFILEINFO sfi = new SHFILEINFO();
				if (SHGetFileInfo(pidl, 0, ref sfi, (uint)Marshal.SizeOf(sfi), SHGFI_PIDL | SHGFI_DISPLAYNAME) != IntPtr.Zero)
				{
					return sfi.szDisplayName;
				}
				return null;
			});
		}

		private static Bitmap IconHandleToBitmap(IntPtr hIcon)
		{
				Icon icon = Icon.FromHandle(hIcon);
				Bitmap image = icon.ToBitmap();
				// 元のアイコンは破棄
				if (!DestroyIcon(hIcon))
				{
					Dbg.Abort("DestroyIcon Failed");
				}
				return image;
		}

		public static Bitmap GetIconBitmap(byte[] bytes, bool large)
		{
			if (bytes == null)	// null を許容
			{
				return null;
			}
			return ForPidl(bytes, delegate(IntPtr pidl){
				SHFILEINFO sfi = new SHFILEINFO();
				if (SHGetFileInfo(pidl, 0, ref sfi, (uint)Marshal.SizeOf(sfi),
					SHGFI_PIDL | SHGFI_ICON | (large ? SHGFI_LARGEICON : SHGFI_SMALLICON)) != IntPtr.Zero)
				{
					return IconHandleToBitmap(sfi.hIcon);
				}
				return null;
			});
		}

		public static Bitmap GetIconBitmap(string path, bool large)
		{
			return GetIconBitmap(PathToPidl(path), large);
		}

		public static void Raise(EventHandler handler)
		{
			if (handler != null)
			{
				handler.Invoke(null, new EventArgs());
			}
		}

		public static T ChooseFromArray<T>(IWin32Window owner, T[] array)
			where T : class
		{
			switch (array.Length)
			{
				case 0:
					return null;
				case 1:
					return array[0];
			}

			ChooseFromArrayForm form = new ChooseFromArrayForm();
			form.Source = array;
			DialogResult res = form.ShowDialog(owner);
			if (res == DialogResult.Cancel)
			{
				return null;
			}
			return (T)form.Selected;
		}

		public static EventHandler ToEventHandler(MethodInvoker func)
		{
			return delegate(object sender, EventArgs e)
			{
				func();
			};
		}

		public static byte[] PathToPidl(string path)
		{
			IntPtr pidl = ILCreateFromPath(path);
			if (pidl == IntPtr.Zero)
			{
				return null;
			}

			int size = (int)ILGetSize(pidl);
			byte[] bytes = new byte[size];
			Marshal.Copy(pidl, bytes, 0, size);

			ILFree(pidl);
			return bytes;
		}

		public static byte[] AddressToPidl(Address addr)
		{
			if (addr.ITEMIDLIST != null)
			{
				return addr.ITEMIDLIST;
			}

			if (addr.Path == null)
			{
				return null;
			}
			return PathToPidl(addr.Path);
		}

		public static int CountOf<T>(IEnumerable<T> e)
		{
			int i = 0;
			foreach (object o in e)
			{
				i++;
			}
			return i;
		}

		public static object GetRegistryValue(string name)
		{
			RegistryKey reg = Plugins.OpenRegistryKey(false);
			if (reg == null)
			{
				return null;
			}
			object obj = reg.GetValue(name);
			reg.Close();

			return obj;
		}

		public static object GetRegistryValue(string name, object defaultValue)
		{
			object ret = GetRegistryValue(name);
			return ret != null ? ret : defaultValue;
		}

		public static void SetRegistryValue(string name, object val)
		{
			RegistryKey key = Plugins.OpenRegistryKey(true);
			key.SetValue(name, val);
			key.Flush();
			key.Close();
		}

		public static void UseWaitCursor(MethodInvoker func)
		{
			Cursor cur = Cursor.Current;
			Cursor.Current = Cursors.WaitCursor;
			try
			{
				func();
			}
			finally
			{
				Cursor.Current = cur;
			}
		}

		public static string BrowseFolder(IntPtr owner, string caption, string initialPath)
		{
			BROWSEINFO bi = new BROWSEINFO();
			bi.hwndOwner = owner;
			//bi.pidlRoot = IntPtr.Zero;
			bi.pszDisplayName = initialPath;
			bi.lpszTitle = caption;
			bi.ulFlags = BIF_NEWDIALOGSTYLE | BIF_SHAREABLE | BIF_EDITBOX | BIF_NONEWFOLDERBUTTON;
			//bi.lpfn = 0;
			//bi.lParam = IntPtr.Zero;
			//bi.iImage = 0;

			IntPtr pidl = SHBrowseForFolder(ref bi);
			if (pidl == IntPtr.Zero)
			{
				return null;
			}
			//if (SHGetPathFromIDList(pidl, buf) != 0)
			//{
			//    path = buf.ToString();
			//}

			try
			{
				return PidlToPath(pidl);
			}
			finally
			{
				Marshal.FreeCoTaskMem(pidl);
			}
		}

		//NOTE: 開いたばかりのウィンドウではnullを返す？（InitializeOpenedWindowが実行されてないかも）
		public static IPluginServer GetPluginServer(object tabBar)
		{
			object manager = Reflection.GetField(tabBar, "pluginManager");
			return (IPluginServer)Reflection.GetField(manager, "pluginServer");
		}

		public static Control GetTabControl(object tabBar)
		{
			return (Control)Reflection.GetField(tabBar, "tabControl1");
		}

		public static void SetRedraw(IntPtr hwnd, bool b)
		{
			SendMessage(hwnd, WM_SETREDRAW, b, IntPtr.Zero);
		}

		public static void EditInformation<T>(IWin32Window owner, string title, Bitmap image)
			where T: class
		{
			T info = Persistence.GetInformation<T>();
			PropertiesForm form = new PropertiesForm();
			form.Text = title;
			if (image != null)
			{
				form.SetIcon(image);
			}
			form.PropertyGrid.SelectedObject = info;
			if (form.ShowDialog(owner) == DialogResult.Cancel)
			{
				return;
			}

			Persistence.SaveInformation(info);
		}

		public static short LowWord(int val)
		{
			return (short)(val & 0xffff);
		}

		public static short HighWord(int val)
		{
			return (short)((val >> 16) & 0xffff);
		}

		public static short LowWord(IntPtr val)
		{
			return LowWord((int)val.ToInt64());
		}

		public static short HighWord(IntPtr val)
		{
			//NOTE: int値がマイナスの場合、64bit環境では ToInt32 で OverflowException が出る
			return HighWord((int)val.ToInt64());
		}

		public static IntPtr GetEditorOwnerWindow(IServiceProvider provider)
		{
			// provider の実体は PropertyGridInternal.GridEntry
			try
			{
				PropertyGrid grid = Reflection.GetProperty(provider, "OwnerGrid") as PropertyGrid;
				if (grid != null)
				{
					return grid.ParentForm.Handle;
				}
			}
			catch (MissingMethodException)
			{
			}

			IUIService uiService = (IUIService)provider.GetService(typeof(IUIService));
			if (uiService == null)
			{
				return IntPtr.Zero;
			}
			IWin32Window win = uiService.GetDialogOwnerWindow();
			if (win == null)
			{
				return IntPtr.Zero;
			}
			return win.Handle;
		}

		public static bool IsVista()
		{
			return Environment.OSVersion.Version.Major >= 6;
		}

		public static ContextMenuStrip CreateContextMenuStrip(PropertyGrid grid)
		{
			ContextMenuStrip menu = new ContextMenuStrip();
			menu.Renderer = Plugins.GetMenuRenderer();
			menu.Items.Add("リセット", null,
				Utility.ToEventHandler(delegate()
				{
					grid.ResetSelectedProperty();
				}));

			StringResource.Apply("PropertyGrid_ContextMenu", menu.Items);

			return menu;
		}

		public static Type GetExternalType(string typeName)
		{
			if (Plugins.ServerAssembly == null)
			{
				return null;
			}
			string name = Assembly.CreateQualifiedName(Plugins.ServerAssembly.FullName, typeName);
			return Type.GetType(name, true);
		}

		public static void ForException(Exception ex)
		{
			ThreadExceptionDialog dlg = new ThreadExceptionDialog(ex);
			dlg.ShowDialog();
		}

		public static void ForException(Exception ex, string caption)
		{
			ThreadExceptionDialog dlg = new ThreadExceptionDialog(ex);
			dlg.Text = caption;
			dlg.ShowDialog();
		}

		public static void Protect(MethodInvoker fun)
		{
			try
			{
				fun();
			}
			catch (Exception ex)
			{
				Utility.ForException(ex);
			}
		}

		public static string GetWindowText(IntPtr hwnd)
		{
			int len = GetWindowTextLength(hwnd);
			StringBuilder buf = new StringBuilder(len + 1);

			int readBytes = GetWindowText(hwnd, buf, buf.Capacity);
			if (readBytes != len)
			{
				throw new OperationFailedException("GetWindowText");
			}
			return buf.ToString();
		}

		[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
		public static extern bool SetWindowText(IntPtr hwnd, String lpString);

		public static IntPtr PointToDword(Point pt)
		{
			return (IntPtr)((pt.X & 0xffff) | ((pt.Y & 0xffff) << 16));
		}

		[DllImport("user32.dll")]
		public static extern bool ScreenToClient(IntPtr hWnd, ref POINT lpPoint);

		[DllImport("user32.dll")]
		static extern short GetAsyncKeyState(System.Windows.Forms.Keys vKey);

		[DllImport("user32.dll")]
		static extern int GetSystemMetrics(uint smIndex);

		private const uint SM_SWAPBUTTON = 23;

		public static MouseButtons KeyToMouseButton(Keys key)
		{
			switch (key)
			{
				case Keys.LButton:
					return MouseButtons.Left;
				case Keys.MButton:
					return MouseButtons.Middle;
				case Keys.RButton:
					return MouseButtons.Right;
				case Keys.XButton1:
					return MouseButtons.XButton1;
				case Keys.XButton2:
					return MouseButtons.XButton2;
			}
			return MouseButtons.None;
		}

		private static MouseButtons SwapMouseButton(MouseButtons button)
		{
			if (GetSystemMetrics(SM_SWAPBUTTON) != 0)
			{
				if (button == MouseButtons.Left)
				{
					return MouseButtons.Right;
				}
				else if (button == MouseButtons.Right)
				{
					return MouseButtons.Left;
				}
			}
			return button;
		}

		public static MouseButtons GetPressedMouseButtons()
		{
			Keys[] keys = new Keys[] {
				Keys.LButton, Keys.MButton, Keys.RButton, Keys.XButton1, Keys.XButton2
			};

			MouseButtons buttons = MouseButtons.None;
			foreach (Keys key in keys)
			{
				if (GetAsyncKeyState(key) < 0)
				{
					// 論理ボタンに直す
					buttons = buttons | SwapMouseButton(KeyToMouseButton(key));
				}
			}
			return buttons;
		}

		public static bool IsButtonPressed(MouseButtons button)
		{
			return (GetPressedMouseButtons() & button) != MouseButtons.None;
		}

		[DllImport("user32.dll")]
		private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);

		[DllImport("user32.dll", SetLastError = true)]
		private static extern int GetWindowLong(IntPtr hWnd, int nIndex);

		private const int GWL_STYLE = -16;

		public static void ModifyStyle(IntPtr hwnd, int remove, int add)
		{
			int style = GetWindowLong(hwnd, GWL_STYLE);
			style &= ~remove;
			style |= add;
			SetWindowLong(hwnd, GWL_STYLE, style);
		}

		[DllImport("user32.dll")]
		private static extern bool ClientToScreen(IntPtr hWnd, ref POINT lpPoint);

		public static Point PointToClient(IntPtr hwnd, Point screenPos)
		{
			POINT pt = new POINT(screenPos);
			ScreenToClient(hwnd, ref pt);
			return pt.ToPoint();
		}

		public static Point PointToScreen(IntPtr hwnd, Point screenPos)
		{
			POINT pt = new POINT(screenPos);
			ClientToScreen(hwnd, ref pt);
			return pt.ToPoint();
		}

		public static bool Swap<T>(IList<T> list, T a, T b)
		{
			int index = list.IndexOf(a);
			if (index == -1)
			{
				return false;
			}
			list.RemoveAt(index);
			list.Insert(index, b);
			return true;
		}

		public static void EnableDoubleBuffer(Control c)
		{
			Reflection.SetProperty(c, "DoubleBuffered", true);
		}
	}
}
